﻿
namespace MefContrib.Models.Provider.Tests
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using NUnit.Framework;

    [TestFixture]
    public class ComposableFieldTests
    {
        [Test]
        public void DeclaringTypeShouldReturnCorrectType()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsTrue(composable.DeclaringType == typeof(FakeFieldPart));
        }

        [Test]
        public void FieldShouldNotBeNull()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsNotNull(composable.Field);
        }


        [Test]
        public void NonStaticFieldShouldRequireAnInstance()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsTrue(composable.IsInstanceNeeded);
        }

        [Test]
        public void NonExportTypeFieldShouldNotBeLazy()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsFalse(composable.IsLazyType);
        }

        [Test]
        public void FieldShouldBeReadable()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsTrue(composable.IsReadable);
        }


        [Test]
        public void NonConstAndReadonlyFieldShouldBeWritable()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsTrue(composable.IsWritable);
        }

        [Test]
        public void FieldShouldHaveCorrectReturnType()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsTrue(composable.ReturnType == typeof(string));
        }

        [Test]
        public void GetValueShouldReturnTheCorrectValue()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            FakeFieldPart part =
                new FakeFieldPart();

            string returnValue =
                composable.GetValue(part) as string;

            Assert.AreEqual(returnValue, "plainField");
        }

        [Test]
        public void SetValueShouldNotFailOnWritableField()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            FakeFieldPart part =
                new FakeFieldPart();

            composable.SetValue(part, "dummy value");

            Assert.IsTrue(true);

            string returnValue =
                composable.GetValue(part) as string;

            Assert.AreEqual(returnValue, "dummy value");
        }

        [Test]
        public void StaticFieldShouldNeverNeedAnInstance()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("staticField")[0]);

            Assert.IsFalse(composable.IsInstanceNeeded);
        }

        [Test]
        public void ConstantFieldShouldNotBeWritable()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("constField")[0]);

            Assert.IsFalse(composable.IsWritable);
        }

        [Test, ExpectedException(typeof(InvalidOperationException))]
        public void ConstantFieldShouldThrowExceptionWhenValueIsSet()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("constField")[0]);

            FakeFieldPart part =
                new FakeFieldPart();

            composable.SetValue(part, "dummy value");
        }

        [Test]
        public void ReadonlyFieldShouldNotBeWritable()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("readOnlyField")[0]);

            Assert.IsFalse(composable.IsWritable);
        }

        [Test, ExpectedException(typeof(InvalidOperationException))]
        public void ReadonlyFieldShouldThrowExceptionWhenValueIsSet()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("readOnlyField")[0]);

            FakeFieldPart part =
                new FakeFieldPart();

            composable.SetValue(part, "dummy value");
        }
        
        [Test]
        public void IsLazyTypeShouldBeFalseForNonLazyField()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("plainField")[0]);

            Assert.IsFalse(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeTrueForLazyField()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("lazyField")[0]);

            Assert.IsTrue(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeFalseForNonLazyCollectionField()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("nonLazyCollectionField")[0]);

            Assert.IsFalse(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeTrueForLazyCollectionField()
        {
            ComposableField composable =
                new ComposableField(typeof(FakeFieldPart).GetMember("lazyCollectionField")[0]);

            Assert.IsTrue(composable.IsLazyType);
        }
    }

    public class FakeFieldPart
    {
        public string plainField = "plainField";
        public const string constField = "constField";
        public static string staticField = "staticField";
        public readonly string readOnlyField = "readOnlyField";
        public Lazy<string> lazyField = new Lazy<string>(() => string.Empty);
        public List<string> nonLazyCollectionField = new List<string>();
        public IEnumerable<Lazy<string>> lazyCollectionField = new List<Lazy<string>>();
    }
}